/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = frprotocol_detect.c                                */
/*  NOTE      = 协议判断                                           */
/*  DATE      = 2014/10/29                                         */
/*******************************************************************/

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <dlfcn.h>

#include "frcomm.h"
#include "frmem.h"
#include "frstr.h"
#include "frlog.h"
#include "conntrack.h"
#include "frame.h"
#include "frame_params.h"
#include "module.h"
#include "frct_comm.h"
#include "frct_mng.h"
#include "frmodule_mng.h"
#include "frproto_mng.h"
#include "frproto_l7default.h"
#include "frstat_module.h"
#include "frstat_system.h"
#include "frstat_server.h"

/*******************************************************************************
*  FUNC     :  从指定位置开始删除解析协议时，更新统计信息
*  ARGS     :  p_st_ct              (Conntrack *)
*           :  ui_idx               (协议从0开始)
*  RTN      :  FR_SUCC:成功,FR_FAIL:失败
*  NOTE     :
*******************************************************************************/
FR_STATIC int __frct_stat_system_update(Conntrack *p_st_ct
                                          , int i_syn
                                          , int i_established
                                          , int i_disconnect
                                          , int i_destroy)
{
    L7_analyzer *p_st_l7_analyzer;

    if(p_st_ct==NULL)
        return FR_FAIL;

    p_st_l7_analyzer = &p_st_ct->st_l7_analyzer;
    if(p_st_l7_analyzer->p_st_l7_module == NULL)
        return FR_FAIL;

    frstat_system_module_conn_add(p_st_l7_analyzer->p_st_l7_module->i_l7_proto, i_syn, i_established, i_disconnect, i_destroy);
    frstat_server_add(p_st_ct->dst_ip, p_st_ct->dst_port, p_st_l7_analyzer->p_st_l7_module->i_l7_proto, i_established-i_disconnect-i_destroy, 0, 0);

    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  从指定位置开始删除解析协议时，更新统计信息
*  ARGS     :  p_st_hdr_l7_proto    (Frlist_header *)
*  RTN      :  FR_SUCC:成功,FR_FAIL:失败
*  NOTE     :  需要加锁( p_st_ct->lock)
*******************************************************************************/
int frct_stat_system_update(Conntrack *p_st_ct
                              , int i_syn
                              , int i_established
                              , int i_disconnect
                              , int i_destroy)
{
    return __frct_stat_system_update(p_st_ct, i_syn, i_established, i_disconnect, i_destroy);
}

/*******************************************************************************
*  FUNC     :  更新ct上已经识别的协议
*  ARGS     :  p_st_ct              (Conntrack *)
*           :  p_st_l7_module       (L7_module*)
*  RTN      :  FR_SUCC:成功,FR_FAIL:失败
*  NOTE     :
*******************************************************************************/
int frct_l7proto_update(Conntrack *p_st_ct, L7_module *p_st_l7_module)
{
    L7_analyzer *p_st_l7_analyzer=NULL;
    
    if(p_st_ct == NULL || p_st_l7_module == NULL)
        return FR_FAIL;

    p_st_l7_analyzer = &p_st_ct->st_l7_analyzer;
    if(p_st_l7_analyzer->p_st_l7_module == NULL)
    {
        frct_l7analyzer_set(p_st_l7_analyzer, p_st_l7_module);
        frct_stat_system_update(p_st_ct, 0, 1, 0, 0);
        return FR_SUCC;
    }
    else
    {
        if(p_st_l7_analyzer->p_st_l7_module == p_st_l7_module)
            return FR_SUCC;

        __frct_stat_system_update(p_st_ct, 0, -1, 0, 0);
        frct_l7analyzer_free(p_st_l7_analyzer);
        frct_l7analyzer_set(p_st_l7_analyzer, p_st_l7_module);

        frstat_system_module_conn_add(p_st_l7_module->i_l7_proto, 0, 1, 0, 0);
        frstat_server_add(p_st_ct->dst_ip, p_st_ct->dst_port, p_st_l7_module->i_l7_proto, 1, 0, 0);
        return FR_SUCC;
    }

    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  释放协议
*  ARGS     :  p_st_ct              (Conntrack *)
*  RTN      :
*  NOTE     :
*******************************************************************************/
void frct_l7proto_destory(Conntrack *p_st_ct)
{
    if(p_st_ct == NULL)
        return;

    //主动close时,已经更新状态
    if(p_st_ct->flg_status != APM_CONNECT_CLOSED)
        __frct_stat_system_update(p_st_ct, 0, 0, 0, 1);

    frct_l7analyzer_free(&p_st_ct->st_l7_analyzer);
    return;
}

/*******************************************************************************
*  FUNC     :  在指定协议中查询,如果查询到则设定
*  ARGS     :  p_st_ct              (Conntrack *)
            :  p_st_lst_hdr         (检查链表 节点存放Frmodule_mng)
            :  p_st_module_mng_exp  (如果和该节点一致,则跳过该节点,可以为NULL)
*  RTN      :  成功:返回管理节点Frmodule_mng ; FR_FAIL:NULL
*  NOTE     :
*******************************************************************************/
FR_STATIC Frmodule_mng *__frproto_list_detect(Conntrack *p_st_ct, Apm_packet *p_st_packet
                                            , Frlist_header *p_st_lst_hdr, Frmodule_mng *p_st_module_mng_exp)
{
    Frlist_node *p_st_node=NULL;
    Frmodule_mng *p_st_module_mng_tmp=NULL;
    int i_fun_ret;

    if(p_st_ct == NULL || p_st_lst_hdr == NULL)
        return NULL;

    p_st_node = FRLIST_FIRST_NODE(p_st_lst_hdr);
    while(p_st_node)
    {
        p_st_module_mng_tmp =  (Frmodule_mng*)FRLIST_GET_NODE_DATA(p_st_node);
        if( (p_st_module_mng_exp && p_st_module_mng_exp == p_st_module_mng_tmp)
            || p_st_module_mng_tmp==NULL || p_st_module_mng_tmp->p_st_l7_module == NULL
            || p_st_module_mng_tmp->p_st_l7_module->l7_detect == NULL)
        {
            p_st_node = FRLIST_NEXT_NODE(p_st_node);
            continue;
        }
        //协议识别
        i_fun_ret = p_st_module_mng_tmp->p_st_l7_module->l7_detect(p_st_ct, p_st_packet);
        //frwarn("[%s].is i_ret=%04x\n", p_st_module_mng_tmp->p_st_l7_module->p_module_name, i_fun_ret);
        if(i_fun_ret  == APM_L7_IS_YES)
        {
            return p_st_module_mng_tmp;
        }
        p_st_node = FRLIST_NEXT_NODE(p_st_node);
    }
    
    return NULL;
}

/*******************************************************************************
*  FUNC     :  改变l7协议状态
*  ARGS     :  p_st_ct      (Conntrack *)
*  RTN      :  
*  NOTE     :
*******************************************************************************/
void frct_l7proto_change_status(Conntrack *p_st_ct
                               , int i_flag_module_status
                               , unsigned int ui_define_ver
                               , int i_flag_l7proto_status)
{
    if(p_st_ct == NULL)
        return;
    
    p_st_ct->i_flag_module_status = i_flag_module_status;
    p_st_ct->ui_define_ver = ui_define_ver;
    p_st_ct->i_flag_l7proto_status = i_flag_l7proto_status;
    
    return;
}

/*******************************************************************************
*  FUNC     :  第1层协议的判断
*  ARGS     :  p_st_ct      (Conntrack *)
            :  p_st_packet (协议分析用数据)
*  RTN      :  FR_SUCC:成功,FR_FAIL:失败
*  NOTE     :  需要加锁( p_st_ct->lock)
*******************************************************************************/
int frproto_l7_detect(Conntrack *p_st_ct, Apm_packet *p_st_packet)
{
    Frlist_header st_lst_hdr_pri, st_lst_hdr_general;
    Frmodule_mng *p_st_module_mng_tmp=NULL, *p_st_module_mng_known=NULL, *p_st_module_mng_default=NULL;
    L7_module *p_st_l7_module_find=NULL;
    unsigned int ui_proto_mng_ver=0;
    int i_fun_ret=0, i_ret = FR_SUCC;
    int i_flag_dst_is_srv=-1;
    
    if(p_st_ct == NULL || p_st_packet == NULL)
        return FR_FAIL;

    frlist_header_init(&st_lst_hdr_pri);
    frlist_header_init(&st_lst_hdr_general);

    /* 对于已经识别的协议，可能存在协议更新的情况，需要再次判断
     * 对于协议识别识别的CT，也需要再次协议判断(同步模型除外) */
    if( (p_st_ct->i_flag_l7proto_status == APM_L7PROTO_STATUS_DONE
           && p_st_ct->st_l7_analyzer.p_st_l7_module
           && p_st_ct->st_l7_analyzer.p_st_l7_module->i_l7_proto != APM_L7_SYNCMODULE)
        || p_st_ct->i_flag_l7proto_status == APM_L7PROTO_STATUS_FAIL )
    {
        i_fun_ret = 0;
        //协议由用户指定
        if(p_st_ct->i_flag_module_status == APM_MODULE_STATUS_DEFINE)
        {
            i_fun_ret = frproto_define_is_same(p_st_ct->ip_proto, p_st_ct->dst_ip, p_st_ct->dst_port, p_st_ct->ui_define_ver);
            if(i_fun_ret == 0)
                return FR_SUCC;
        }

        if(p_st_ct->i_flag_module_status == APM_MODULE_STATUS_DEFINE && i_fun_ret == 1)
        {
            //获取用户指定的协议以及版本号
            i_flag_dst_is_srv=-1;
            frproto_define_get(p_st_ct->ip_proto, p_st_ct->dst_ip, p_st_ct->dst_port, &p_st_l7_module_find, &ui_proto_mng_ver);
            if( p_st_l7_module_find == NULL)
            {
                frproto_define_get(p_st_ct->ip_proto, p_st_ct->src_ip, p_st_ct->src_port, &p_st_l7_module_find, &ui_proto_mng_ver);
                if( p_st_l7_module_find == NULL)
                    return FR_SUCC;
                else
                    i_flag_dst_is_srv=0;
            }
            else
                i_flag_dst_is_srv=1;
            
            if(ui_proto_mng_ver == p_st_ct->ui_define_ver)
                return FR_SUCC;

            if(i_flag_dst_is_srv==1)
                frcap_set_cs_srv(p_st_ct, p_st_ct->dst_ip, p_st_ct->dst_port);
            else
                frcap_set_cs_srv(p_st_ct, p_st_ct->src_ip, p_st_ct->src_port);
            frct_l7proto_update(p_st_ct, p_st_l7_module_find);
            frct_l7proto_change_status(p_st_ct, APM_MODULE_STATUS_DEFINE, ui_proto_mng_ver, APM_L7PROTO_STATUS_DONE);
            
            return FR_SUCC;
        }
        else //协议自己识别/未识别/定义协议已经删除
        {
            //获取用户指定的协议
            i_flag_dst_is_srv=-1;
            frproto_define_get(p_st_ct->ip_proto, p_st_ct->dst_ip, p_st_ct->dst_port, &p_st_l7_module_find, &ui_proto_mng_ver);
            if( p_st_l7_module_find == NULL)
            {
                frproto_define_get(p_st_ct->ip_proto, p_st_ct->src_ip, p_st_ct->src_port, &p_st_l7_module_find, &ui_proto_mng_ver);
                if( p_st_l7_module_find == NULL)
                    return FR_SUCC;
                else
                    i_flag_dst_is_srv=0;
            }
            else
                i_flag_dst_is_srv=1;

            if(i_flag_dst_is_srv==1)
                frcap_set_cs_srv(p_st_ct, p_st_ct->dst_ip, p_st_ct->dst_port);
            else
                frcap_set_cs_srv(p_st_ct, p_st_ct->src_ip, p_st_ct->src_port);
            frct_l7proto_update(p_st_ct, p_st_l7_module_find);
            frct_l7proto_change_status(p_st_ct, APM_MODULE_STATUS_DEFINE, ui_proto_mng_ver, APM_L7PROTO_STATUS_DONE);
            
            return FR_SUCC;
        }
    }

    //协议没有识别,或还需要多次判断
    frproto_define_get(p_st_ct->ip_proto, p_st_ct->dst_ip, p_st_ct->dst_port, &p_st_l7_module_find, &ui_proto_mng_ver);
    //指定协议里存在
    if( p_st_l7_module_find != NULL)
    {
        frcap_set_cs_srv(p_st_ct, p_st_ct->dst_ip, p_st_ct->dst_port);
        frct_l7proto_update(p_st_ct, p_st_l7_module_find);
        frct_l7proto_change_status(p_st_ct, APM_MODULE_STATUS_DEFINE, ui_proto_mng_ver, APM_L7PROTO_STATUS_DONE);
        return FR_SUCC;
    }
    else
    {
        frproto_define_get(p_st_ct->ip_proto, p_st_ct->src_ip, p_st_ct->src_port, &p_st_l7_module_find, &ui_proto_mng_ver);
        if( p_st_l7_module_find != NULL)
        {
            frcap_set_cs_srv(p_st_ct, p_st_ct->src_ip, p_st_ct->src_port);
            frct_l7proto_update(p_st_ct, p_st_l7_module_find);
            frct_l7proto_change_status(p_st_ct, APM_MODULE_STATUS_DEFINE, ui_proto_mng_ver, APM_L7PROTO_STATUS_DONE);
            return FR_SUCC;
        }
    }
    
    //同步模型时,确认数据包不够,需要再次确认
    if(p_st_ct->st_l7_analyzer.p_st_l7_module
       && p_st_ct->st_l7_analyzer.p_st_l7_module->i_l7_proto == APM_L7_SYNCMODULE)
    {
        if(p_st_packet->i_direction == FR_DIRECT_CLIENT2SRV
            && p_st_ct->ui_detect_data_cnt[0] >= st_apm_params.ui_l7_detect_max_times)
            return FR_SUCC;
        if(p_st_packet->i_direction == FR_DIRECT_SRV2CLIENT
            && p_st_ct->ui_detect_data_cnt[1] >= st_apm_params.ui_l7_detect_max_times)
            return FR_SUCC;
    }

    //增加该ct的协议判断次数
    if(p_st_packet->us_is_detected == FR_NO)
    {
        if(p_st_packet->i_direction == FR_DIRECT_CLIENT2SRV)
            p_st_ct->ui_detect_data_cnt[0] += 1;
        else
            p_st_ct->ui_detect_data_cnt[1] += 1;
    }

    //已经识别的协议
    //先用已经识别过的协议判断
    frproto_known_get(p_st_ct->ip_proto, p_st_ct->dst_ip, p_st_ct->dst_port, &p_st_l7_module_find);
    if( p_st_l7_module_find != NULL && p_st_l7_module_find->l7_detect)
    {
        p_st_packet->us_is_detected = FR_YES;
        i_fun_ret = p_st_l7_module_find->l7_detect(p_st_ct, p_st_packet);
        frdbg("[%s].is i_ret=%04x\n", p_st_l7_module_find->p_module_name, i_fun_ret);
        if(i_fun_ret  == APM_L7_IS_YES)
        {
            frct_l7proto_update(p_st_ct, p_st_l7_module_find);
            frct_l7proto_change_status(p_st_ct, APM_MODULE_STATUS_AUOTODETECT, 0, APM_L7PROTO_STATUS_DONE);
            return FR_SUCC;
        }
    }

    //获取按端口组织的优先检查协议链表和普通检查协议链表
    frproto_get_chklist(p_st_ct->ip_proto, p_st_ct->dst_port,p_st_ct->src_port, &st_lst_hdr_pri, &st_lst_hdr_general);

    p_st_packet->us_is_detected = FR_YES;
    //优先判断协议
    p_st_module_mng_tmp =  __frproto_list_detect(p_st_ct, p_st_packet, &st_lst_hdr_pri, p_st_module_mng_known);
    //普通判断协议
    if(p_st_module_mng_tmp == NULL)
        p_st_module_mng_tmp =  __frproto_list_detect(p_st_ct, p_st_packet, &st_lst_hdr_general, p_st_module_mng_known);
    
    i_ret = FR_FAIL;
    if(p_st_module_mng_tmp)
    {
        frct_l7proto_update(p_st_ct, p_st_module_mng_tmp->p_st_l7_module);
        frct_l7proto_change_status(p_st_ct, APM_MODULE_STATUS_AUOTODETECT, 0, APM_L7PROTO_STATUS_DONE);

        i_ret = FR_SUCC;
    }
    else
    {
        //超过最大失败次数,设定为默认协议
        if(p_st_ct->ui_done_data_cnt >= st_apm_params.ui_l7_detect_max_times)
        {
            p_st_module_mng_default = frproto_l7default_find(p_st_ct->ip_proto);
            if(p_st_module_mng_default == NULL)
                p_st_module_mng_default = frproto_l7default_find(IPPROTO_TCP|IPPROTO_UDP);

            if(p_st_module_mng_default)
            {
                //frwarn("proto_chk_cnt=%d use default module %s\n", p_st_ct->ui_done_data_cnt, p_st_module_mng_default->p_st_l7_module.p_module_name);
                frct_l7proto_update(p_st_ct, p_st_module_mng_default->p_st_l7_module);
                p_st_ct->i_flag_l7proto_status = APM_L7PROTO_STATUS_DONE;
                i_ret = FR_SUCC;
            }
            else
                p_st_ct->i_flag_l7proto_status = APM_L7PROTO_STATUS_FAIL;
        }
    }
    
    frlist_remove_all(&st_lst_hdr_pri, NULL);
    frlist_remove_all(&st_lst_hdr_general, NULL);
    
    return i_ret;
}

/*******************************************************************************
*  FUNC     :  协议解码
*  ARGS     :  p_st_ct       (Conntrack *)
*           :  p_st_packet  (Analyz_data_in *, 协议判断用数据)
*           :  p_st_data_out (Analyz_data_out *, 解析后出力数据)
*  RTN      :  l7_analyze的返回值
*  NOTE     :
*******************************************************************************/
int frproto_l7_analyze(Conntrack *p_st_ct
                     , Apm_packet *p_st_packet
                     , Frhash_header  *p_st_hash_report)
{
    L7_analyzer *p_st_l7_analyzer=NULL;
    L7_module *p_st_l7_module=NULL;
    int i_ret_fun=APM_L7_ANALYZE_FAIL;
    int i_requests=0, i_responses=0, i_succs=0, i_fails = 0;
    
    if(p_st_ct == NULL || p_st_packet == NULL
        || p_st_hash_report==NULL)
        return APM_L7_ANALYZE_FAIL;

    p_st_l7_analyzer = frct_get_L7analyzer(p_st_ct);
    if(p_st_l7_analyzer)
        p_st_l7_module = p_st_l7_analyzer->p_st_l7_module;
    if(p_st_l7_module == NULL || p_st_l7_module->l7_analysis == NULL)
        return APM_L7_ANALYZE_FAIL;

    i_ret_fun = p_st_l7_module->l7_analysis(p_st_ct, p_st_packet, p_st_hash_report);
    //frwarn("p_st_l7_module->l7_analyze=%d(%s %p) ret=0x%x\n", p_st_l7_module->i_l7_proto, p_st_l7_module->p_module_name, p_st_l7_module->l7_analyze, i_ret_fun);
    switch(i_ret_fun)
    {
        case APM_L7_ANALYZE_REDETECT:
            i_succs = 1;
            break;
        case APM_L7_ANALYZE_FAIL:
            i_fails = 1;
            break;
        default:
            if( APM_L7_ANALYZE_IS_SUCC(i_ret_fun) )
                i_succs = 1;
            if( APM_L7_ANALYZE_IS_REQ(i_ret_fun) )
                i_requests = 1;
            if( APM_L7_ANALYZE_IS_RESP(i_ret_fun) )
                i_responses = 1;
    }
    if(i_requests > 0 || i_responses > 0 || i_succs > 0  || i_fails > 0 )
    {
        if(i_fails ==0 && i_succs == 0 && (i_requests > 0 || i_responses > 0) )
            i_succs = 1;
        frstat_module_add(p_st_ct->ip_proto, p_st_l7_module->i_l7_proto, i_requests, i_responses, i_succs, i_fails);
    }
    
    return i_ret_fun;
}

